Supports Anymal-D Rough terrain training in newton#5225
Supports Anymal-D Rough terrain training in newton#5225ooctipus wants to merge 2 commits intoisaac-sim:developfrom
Conversation
kellyguo11
left a comment
There was a problem hiding this comment.
PR Review: Supports Anymal-D Rough Terrain Training in Newton
Overview
This is a large, multi-feature PR (+5344/-3744 across 87 files) that bundles together several cross-cutting changes needed for Newton-based rough terrain locomotion training. The scope spans:
- Newton XformPrimView (
NewtonSiteXformPrimView) — GPU-resident site-based transform tracking via Warp kernels - Fabric XformPrimView (
FabricXformPrimView) — Fabric-accelerated variant for PhysX backend - XformPrimView contract tests (
xform_contract_tests.py) — Shared test invariants for all backends - Newton collision pipeline config (
NewtonCollisionPipelineCfg,HydroelasticSDFCfg) — Expose Newton collision parameters - Friction/restitution randomization —
set_friction_index/mask,set_restitution_index/maskfor Newton assets +set_material_properties_index/maskfor PhysX - Material randomization refactor — Backend-aware
randomize_rigid_body_material+ newrandomize_rigid_body_inertia+ refactoredrandomize_rigid_body_collider_offsets - RayCaster sensor spawning —
RayCasterXformCfgso sensors get their own non-physics Xform (Newton requires this) - New terminations —
body_lin_vel_out_of_limit,body_ang_vel_out_of_limit - Task config updates — All locomotion rough-terrain configs updated for raycaster child paths + Newton solver configs for AnymalD
- Velocity env restructure —
StartupEventsCfgmerged intoEventsCfg, events no longerMISSING
Should-Fix Issues
1. Version mismatch in isaaclab_newton extension.toml vs CHANGELOG
extension.toml sets version = "0.5.11", but the CHANGELOG has entries for 0.5.11, 0.5.12, and 0.5.13. The version in extension.toml should match the latest changelog entry (0.5.13).
2. Changelog entry for isaaclab_physx version ordering inconsistency
The CHANGELOG adds entries for 0.5.15 and 0.5.14, but extension.toml is bumped from 0.5.13 → 0.5.14. The 0.5.15 changelog entry documents work in this PR but there's no corresponding version bump, suggesting either the entry should be 0.5.14 or the toml needs to go to 0.5.15.
3. velocity_env_cfg.py: events field changed from MISSING to EventsCfg()
LocomotionVelocityRoughEnvCfg.events changes from MISSING to EventsCfg(). This is a breaking change — all downstream configs that previously had to supply events (like AnymalB, AnymalC, Spot, etc. not shown in this PR) will now silently get the base defaults. Configs that override events with their own dataclass (e.g., the old AnymalDPhysxEventsCfg(EventsCfg, StartupEventsCfg) pattern) are no longer needed since startup events moved into EventsCfg, but any external config relying on MISSING to enforce explicit event configuration will break silently.
Additionally, StartupEventsCfg is removed. Code outside the repo that inherits from it will fail at import.
4. randomize_rigid_body_material: Newton path uses set_friction_mask / set_restitution_mask (full data every call)
In _call_newton, the code always passes the full self.default_friction / self.default_restitution tensors via set_*_mask(), even when only a subset of environments changed. This writes the entire (num_instances × num_shapes) buffer every call. Consider using set_friction_index / set_restitution_index with only the env_ids that need updating, similar to how the PhysX path passes env_ids to set_material_properties_index.
5. NewtonSiteXformPrimView._gather_scales kernel has O(N×S) complexity
The _gather_scales Warp kernel does a linear scan over all shapes for every site (O(sites × shapes)). For articulations with hundreds of shapes, this is slow. Consider building a precomputed site_body → first_shape index at init time.
Suggestions
6. PR scope — consider splitting
This PR bundles at least 5 independent features. Each would be easier to review and less risky to merge independently:
- XformPrimView backends + contract tests
- Collision pipeline config
- Friction/restitution APIs + material randomization refactor
- RayCaster spawning refactor
- AnymalD Newton config + velocity env restructure
7. randomize_rigid_body_collider_offsets — Newton path accesses internal attributes
The Newton implementation reads/writes shape_margin and shape_gap via root_view.get_attribute() / root_view.set_attribute() with SimulationManager.get_model(). This couples the randomization term to Newton internals. A TODO comment acknowledging this would help.
8. base_com preset uses newton=None in velocity_env_cfg
The PR disables CoM randomization for Newton (base_com = preset(default=EventTerm(...), newton=None)). The PR description mentions "we still have to disable randomize_rigid_body_com, as turning it on will cause levitating robot". This should be documented as a known limitation with a TODO or link to a tracking issue.
9. Test comparison with Isaac Sim — reduced coverage
test_compare_get_world_poses_with_isaacsim and the remaining Isaac Sim comparison tests were heavily simplified — quaternion comparisons removed, local pose and set-pose comparisons deleted entirely. The contract tests partially compensate, but backend-specific behavioral parity with Isaac Sim is no longer tested.
10. UsdXformPrimView.set_world_poses — partial-update edge case
When only positions is provided (without orientations), the code reads the current world transform, replaces translation, then decomposes back to local. This round-trip through Gf.Matrix4d may accumulate floating-point drift over many calls. Consider documenting this or caching.
11. Documentation checkbox unchecked
The PR checklist shows [ ] I have made corresponding changes to the documentation. The raycaster tutorial docs are updated, but the new APIs (set_friction_index, NewtonCollisionPipelineCfg, RayCasterXformCfg, new terminations, randomize_rigid_body_inertia) should have corresponding API docs or migration guide entries.
Positive Notes
- The contract test pattern (
xform_contract_tests.pywithViewBundlefixture) is excellent engineering — ensures all backends satisfy the same invariants - The
NewtonSiteXformPrimViewdesign is clean: ancestor-walk resolution at init, GPU-only pose compute at runtime - The raycaster spawning refactor (
RayCasterXformCfg) is a good pattern — decoupling sensor attachment from physics prims - Backend-aware material randomization with clean PhysX/Newton dispatch
- Good deprecation handling with auto-redirect for legacy raycaster paths
f9bfc0e to
1a37500
Compare
Ablation Study Results — PR #5225 vs PR #5248 mechanism overlapTL;DR: The Ran a 6-variant ablation on Anymal-D Newton (Newton 1.1.0 — this PR's pin), 300 iter each, 4096 envs, single-variable changes via worktrees. Findings on which #5225 features are load-bearing, and how they interact with PR #5248's Setup
Results
InterpretationsA4 (CRASH): confirmed A3 vs A5 (the interesting pair): both
The event computes A1 (NaN termination): never fires on Anymal-D normal training (already covered by A2 (terrain border): subdivided grid border gives ~+1.8 reward over Suggested changes to this PR
Caveats / scope
|
|
FYI @ooctipus — the root cause of the Root cause
Upstream
Verification (2026-04-22, this VM)End-to-end with PR #2332 applied +
Fix not only prevents the collapse — it exceeds the WAR baseline by +17%. Follow-upOnce PR #2332 merges and ships in a Newton release, the |
There was a problem hiding this comment.
🤖 Isaac Lab Review Bot
Summary
This PR adds Newton physics backend support for Anymal-D rough terrain training by: (1) adding new body velocity termination functions, (2) rewriting the terrain border generation to use finer-grained meshes for Newton compatibility, (3) refactoring event configurations to use preset patterns for backend-specific settings, and (4) adding Newton-specific physics configuration. The implementation is mostly correct but has several issues that need attention.
Architecture Impact
- terminations.py: New
body_lin_vel_out_of_limitandbody_ang_vel_out_of_limitfunctions are public API additions. Any environment can now use these for early termination on physics solver instabilities. - terrain_generator.py: The border generation change affects ALL terrain generation, not just Newton. The old
make_borderfunction is no longer imported, which could break external code depending on that import pattern. - velocity_env_cfg.py: The
StartupEventsCfgclass is removed, which is a breaking change for any code that imports or inherits from it. TheEventsCfgnow has startup events with preset patterns for backend selection. - rough_env_cfg.py: The
AnymalDPhysxEventsCfgandAnymalDEventsCfgclasses are removed in favor ofRoughPhysicsCfg.
Implementation Verdict
Minor fixes needed — there are correctness issues with the terrain border implementation and potential device mismatches in termination functions.
Test Coverage
The PR claims tests were added ("I have added tests that prove my fix is effective") but no test files are included in the diff. For a feature PR adding:
- New termination functions (need unit tests)
- Modified terrain generation (need regression tests)
- New physics backend support (need integration tests)
This is a significant gap.
CI Status
No CI checks available yet.
Findings
🔴 Critical: source/isaaclab/isaaclab/terrains/terrain_generator.py:300 — Border strips have gaps at corners
The four border strips are constructed as:
strips = [
_make_grid_strip(-bw, -bw, inner_w + 2 * bw, bw), # bottom
_make_grid_strip(-bw, inner_l, inner_w + 2 * bw, bw), # top
_make_grid_strip(-bw, 0.0, bw, inner_l), # left
_make_grid_strip(inner_w, 0.0, bw, inner_l), # right
]The left/right strips start at y=0.0 and have length inner_l, meaning they don't extend into the corners. The corners are covered by the top/bottom strips, but the left/right strips have a gap from y=-bw to y=0 and from y=inner_l to y=inner_l+bw. The bottom strip starts at y=-bw with height bw, ending at y=0. The left strip starts at y=0, so there's no overlap issue - this is actually correct. Disregard.
🔴 Critical: source/isaaclab/isaaclab/terrains/terrain_generator.py:269-310 — Border height is ignored, terrain border is now flat at z=0
The old implementation used self.cfg.border_height to create a 3D border with configurable height. The new implementation creates a flat mesh at z=0:
vertices = np.zeros((nx * ny, 3)) # z is always 0This silently changes behavior for any configuration that relied on border_height being non-zero. Robots could now walk off the edge of the terrain instead of hitting a wall.
🟡 Warning: source/isaaclab/isaaclab/envs/mdp/terminations.py:166,180 — Missing body_ids initialization check
Both new termination functions use asset_cfg.body_ids directly without checking if it's None:
body_vel = wp.to_torch(asset.data.body_lin_vel_w)[:, asset_cfg.body_ids]When body_ids is None, this will select the first column only (not all columns). Compare with joint_pos_out_of_limit at line 90 which handles this:
if asset_cfg.joint_ids is None:
asset_cfg.joint_ids = slice(None)The same pattern should be applied for body_ids.
🟡 Warning: source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/velocity_env_cfg.py — Breaking API change: StartupEventsCfg removed
The class StartupEventsCfg was removed without deprecation. Any external code importing this class will break:
from isaaclab_tasks.manager_based.locomotion.velocity.velocity_env_cfg import StartupEventsCfg # Now fails🟡 Warning: source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/velocity_env_cfg.py:298-300 — Termination uses body_names=".*" which may be expensive
body_lin_vel = DoneTerm(
func=mdp.body_lin_vel_out_of_limit,
params={"max_velocity": 20.0, "asset_cfg": SceneEntityCfg("robot", body_names=".*")},
)This checks ALL bodies on every step. For robots with many links, this adds overhead. Consider limiting to a subset (e.g., body_names="base") if only checking for solver explosions.
🔵 Improvement: source/isaaclab/isaaclab/terrains/terrain_generator.py:282-296 — Face generation loop could use vectorized operations
The nested loop for face generation is slow for large borders:
faces = []
for i in range(nx - 1):
for j in range(ny - 1):
...
faces.append([v0, v2, v1])
faces.append([v1, v2, v3])This could be vectorized with numpy for better performance:
i, j = np.meshgrid(np.arange(nx-1), np.arange(ny-1), indexing='ij')
v0 = (i * ny + j).flatten()
# ... vectorized face construction🔵 Improvement: source/isaaclab/isaaclab/envs/mdp/terminations.py:159-184 — Missing type hints in TYPE_CHECKING block
The new functions use Articulation in type hints, but the import is only under TYPE_CHECKING. The functions will work at runtime, but static analysis may not recognize the type properly. This matches existing code style, so this is minor.
Greptile SummaryThis PR adds Newton physics backend support for Anymal-D rough terrain locomotion training by replacing the event-based physics selection with a
Confidence Score: 4/5Not safe to merge without addressing the collider_offsets Newton compatibility issue, which will cause a startup crash in Newton mode. One P1 finding: the collider_offsets event uses PhysX-only root_view methods without a Newton guard, which will raise an AttributeError at environment startup when the Newton backend is selected — the primary use-case this PR introduces. All other changes are sound. source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/velocity_env_cfg.py — collider_offsets event needs newton=None guard Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[AnymalDRoughEnvCfg] -->|sim: SimulationCfg| B[RoughPhysicsCfg preset]
B -->|default/physx| C[PhysxCfg\ngpu_max_rigid_patch_count]
B -->|newton| D[NewtonCfg\nMJWarpSolverCfg + CollisionPipeline]
A -->|inherits| E[LocomotionVelocityRoughEnvCfg]
E -->|events| F[EventsCfg]
F --> G[physics_material startup]
F --> H[add_base_mass startup]
F --> I[base_com preset\ndefault=EventTerm / newton=None]
F --> J[collider_offsets startup\n⚠️ no newton guard]
E -->|terminations| K[TerminationsCfg]
K --> L[illegal_contact]
K --> M[body_lin_vel_out_of_limit\nnew - NaN + speed check]
E -->|scene terrain| N[TerrainGenerator]
N --> O[_add_terrain_border\nsubdivided flat-grid strips\nhorizontal_scale spacing]
Reviews (1): Last reviewed commit: "Merge branch 'develop' into newton_rough..." | Re-trigger Greptile |
| collider_offsets = EventTerm( | ||
| func=mdp.randomize_rigid_body_collider_offsets, | ||
| mode="startup", | ||
| params={ | ||
| "asset_cfg": SceneEntityCfg("robot", body_names=".*"), | ||
| "contact_offset_distribution_params": (0.01, 0.01), | ||
| }, | ||
| ) |
There was a problem hiding this comment.
collider_offsets not guarded against Newton backend
randomize_rigid_body_collider_offsets internally calls asset.root_view.get_contact_offsets() and asset.root_view.set_contact_offsets(), which are PhysX-specific methods on the PhysX ArticulationView. Newton's ArticulationView does not expose these methods (no matches exist in the Newton module). Unlike base_com, which is correctly disabled for Newton via preset(default=..., newton=None), collider_offsets is added as a plain EventTerm and will be executed for all backends, likely raising an AttributeError during startup in Newton mode.
collider_offsets = preset(
default=EventTerm(
func=mdp.randomize_rigid_body_collider_offsets,
mode="startup",
params={
"asset_cfg": SceneEntityCfg("robot", body_names=".*"),
"contact_offset_distribution_params": (0.01, 0.01),
},
),
newton=None,
)| faces = [] | ||
| for i in range(nx - 1): | ||
| for j in range(ny - 1): | ||
| v0 = i * ny + j | ||
| v1 = v0 + 1 | ||
| v2 = (i + 1) * ny + j | ||
| v3 = v2 + 1 | ||
| faces.append([v0, v2, v1]) | ||
| faces.append([v1, v2, v3]) | ||
| return trimesh.Trimesh(vertices=vertices, faces=np.array(faces, dtype=np.uint32)) |
There was a problem hiding this comment.
Quadratic Python loop for mesh face generation
The nested for i / for j Python loops build faces as a growing list, costing O(nx × ny) loop iterations at the Python level. For a typical border_width=5 m with horizontal_scale=0.1 m, each strip has ~50 × N cells. While this is a one-time startup cost, a vectorised numpy approach avoids it entirely and makes the intent clearer:
i_idx = np.repeat(np.arange(nx - 1), ny - 1)
j_idx = np.tile(np.arange(ny - 1), nx - 1)
v0 = i_idx * ny + j_idx
v1 = v0 + 1
v2 = (i_idx + 1) * ny + j_idx
v3 = v2 + 1
faces = np.column_stack([
np.stack([v0, v2, v1], axis=1),
np.stack([v1, v2, v3], axis=1),
]).reshape(-1, 3)
return trimesh.Trimesh(vertices=vertices, faces=faces)|
@hujc7 could you also take a look at the potential issues the bot review raised? we can incorporate any fixes needed in your PRs as well |
|
looks like there are some test failures with this PR as well. @hujc7 I think it may be easier to just cherry-pick the required changes from this PR into your PRs so that we can also address the tests. |
Originally Octi's commit on PR isaac-sim#5225. Cherry-picked here so the rough terrain stack does not depend on his still-open WIP PR while he is away. Included from isaac-sim#5225: - source/isaaclab_tasks/.../config/anymal_d/rough_env_cfg.py: Anymal-D SimulationCfg-based RoughPhysicsCfg (MJWarp solver + collision pipeline). The shared parent will hoist this in the next commit. - source/isaaclab_tasks/.../velocity/velocity_env_cfg.py: Hoist physics_material, add_base_mass, base_com startup events into the shared EventsCfg. base_com guarded with preset(newton=None) per ablation A4 (without the gate, 99.99% of episodes terminate from body_lin_vel runaway on Newton). Upstream Newton fix newton-physics/newton#2332 will let us drop the gate once it ships in a release. Dropped from isaac-sim#5225 (no longer needed): - collider_offsets startup event in velocity_env_cfg.py: per ablation A3 (clobbers shape margin via gap = max(0, contact_offset - margin) = 0, costing -3.71 reward on Anymal-D) and greptile P1 (PhysX-only root_view methods, raises AttributeError on Newton without a guard). - body_lin_vel_out_of_limit / body_ang_vel_out_of_limit terminations and their __init__.pyi exports: were a NaN guard for the Newton body_lin_vel runaway when base_com was unguarded. With the preset(newton=None) gate on base_com, the runaway no longer occurs and the guards are unused. - terrain_generator.py subdivided flat-grid border: was a workaround for Newton triangle-collision failures on the box-primitive border. Newton has since improved triangle handling, so the workaround is no longer needed. Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
Originally Octi's commit on PR isaac-sim#5225. Cherry-picked here so the rough terrain stack does not depend on his still-open WIP PR while he is away. Included from isaac-sim#5225: - source/isaaclab_tasks/.../config/anymal_d/rough_env_cfg.py: Anymal-D SimulationCfg-based RoughPhysicsCfg (MJWarp solver + collision pipeline). The shared parent will hoist this in the next commit. - source/isaaclab_tasks/.../velocity/velocity_env_cfg.py: Hoist physics_material, add_base_mass, base_com startup events into the shared EventsCfg. base_com guarded with preset(newton=None) per ablation A4 (without the gate, 99.99% of episodes terminate from body_lin_vel runaway on Newton). Upstream Newton fix newton-physics/newton#2332 will let us drop the gate once it ships in a release. Dropped from isaac-sim#5225 (no longer needed): - collider_offsets startup event in velocity_env_cfg.py: per ablation A3 (clobbers shape margin via gap = max(0, contact_offset - margin) = 0, costing -3.71 reward on Anymal-D) and greptile P1 (PhysX-only root_view methods, raises AttributeError on Newton without a guard). - body_lin_vel_out_of_limit / body_ang_vel_out_of_limit terminations and their __init__.pyi exports: were a NaN guard for the Newton body_lin_vel runaway when base_com was unguarded. With the preset(newton=None) gate on base_com, the runaway no longer occurs and the guards are unused. - terrain_generator.py subdivided flat-grid border: was a workaround for Newton triangle-collision failures on the box-primitive border. Newton has since improved triangle handling, so the workaround is no longer needed. Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
Originally Octi's commit on PR isaac-sim#5225. Cherry-picked here so the rough terrain stack does not depend on his still-open WIP PR while he is away. Included from isaac-sim#5225: - source/isaaclab_tasks/.../config/anymal_d/rough_env_cfg.py: Anymal-D SimulationCfg-based RoughPhysicsCfg (MJWarp solver + collision pipeline). The shared parent will hoist this in the next commit. - source/isaaclab_tasks/.../velocity/velocity_env_cfg.py: Hoist physics_material, add_base_mass, base_com startup events into the shared EventsCfg. base_com guarded with preset(newton=None) per ablation A4 (without the gate, 99.99% of episodes terminate from body_lin_vel runaway on Newton). Upstream Newton fix newton-physics/newton#2332 will let us drop the gate once it ships in a release. Dropped from isaac-sim#5225 (no longer needed): - collider_offsets startup event in velocity_env_cfg.py: per ablation A3 (clobbers shape margin via gap = max(0, contact_offset - margin) = 0, costing -3.71 reward on Anymal-D) and greptile P1 (PhysX-only root_view methods, raises AttributeError on Newton without a guard). - body_lin_vel_out_of_limit / body_ang_vel_out_of_limit terminations and their __init__.pyi exports: were a NaN guard for the Newton body_lin_vel runaway when base_com was unguarded. With the preset(newton=None) gate on base_com, the runaway no longer occurs and the guards are unused. - terrain_generator.py subdivided flat-grid border: was a workaround for Newton triangle-collision failures on the box-primitive border. Newton has since improved triangle handling, so the workaround is no longer needed. Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
Originally Octi's commit on PR isaac-sim#5225. Cherry-picked here so the rough terrain stack does not depend on his still-open WIP PR while he is away. Included from isaac-sim#5225: - source/isaaclab_tasks/.../config/anymal_d/rough_env_cfg.py: Anymal-D SimulationCfg-based RoughPhysicsCfg (MJWarp solver + collision pipeline). The shared parent will hoist this in the next commit. - source/isaaclab_tasks/.../velocity/velocity_env_cfg.py: Hoist physics_material, add_base_mass, base_com startup events into the shared EventsCfg. base_com guarded with preset(newton=None) per ablation A4 (without the gate, 99.99% of episodes terminate from body_lin_vel runaway on Newton). Upstream Newton fix newton-physics/newton#2332 will let us drop the gate once it ships in a release. Dropped from isaac-sim#5225 (no longer needed): - collider_offsets startup event in velocity_env_cfg.py: per ablation A3 (clobbers shape margin via gap = max(0, contact_offset - margin) = 0, costing -3.71 reward on Anymal-D) and greptile P1 (PhysX-only root_view methods, raises AttributeError on Newton without a guard). - body_lin_vel_out_of_limit / body_ang_vel_out_of_limit terminations and their __init__.pyi exports: were a NaN guard for the Newton body_lin_vel runaway when base_com was unguarded. With the preset(newton=None) gate on base_com, the runaway no longer occurs and the guards are unused. - terrain_generator.py subdivided flat-grid border: was a workaround for Newton triangle-collision failures on the box-primitive border. Newton has since improved triangle handling, so the workaround is no longer needed. Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
… on Newton (#5248) # Description Enables Newton rough-terrain locomotion training on all locomotion velocity envs (Go1, Go2, A1, Anymal-B/C/D, H1, Cassie, Digit, G1) on top of [@ooctipus](https://github.com/ooctipus)'s Anymal-D foundation work, cherry-picked from PR #5225. ## Why this PR exists PR #5225 (Octi's draft) added Newton support for Anymal-D rough terrain. The other 9 locomotion envs were left out of scope. Octi is away and his PR has CI failures, so per maintainer guidance ([Kelly Guo's comment](#5225 (comment))) the required changes from #5225 are cherry-picked here so the rough-terrain stack can move forward without depending on his still-open WIP PR. ## Dependencies - PR #5365 — adds `isaaclab.utils.checked_apply`, used by `NewtonManager.create_builder` to forward `NewtonShapeCfg` onto Newton's upstream `ShapeConfig`. Required for stable rough-terrain contact. ## 1. Cherry-pick from #5225 (`614ea2dbb74`) Commit authored by Octi Zhang with `Co-authored-by` trailer. Subset of #5225 — what's kept and dropped: | # | Item | Status | Reason | |---:|---|---|---| | 1 | `anymal_d/rough_env_cfg.py` Anymal-D Newton config | **KEPT** (then hoisted into shared parent in commit 2 below) | Defines the Newton physics shape used for rough terrain | | 2 | `velocity_env_cfg.py` — hoist `physics_material`, `add_base_mass`, `base_com` startup events into shared `EventsCfg` | **KEPT** | All envs need them | | 3 | `base_com` guard `preset(default=..., newton=None)` | **KEPT** | Ablation A4 on #5225 (posted 2026-04-17): without the gate, 99.99% of episodes terminate from `body_lin_vel` runaway on Newton. Upstream Newton fix newton-physics/newton#2332 will let us drop the guard once it ships | | 4 | `velocity_env_cfg.py` — `collider_offsets` startup event | **DROPPED** | (a) Greptile P1 on #5225: PhysX-only `root_view` methods, would `AttributeError` on Newton without a guard. (b) Ablation A3: clobbers the 1cm shape margin set by `RoughPhysicsCfg` (event resets `gap = max(0, contact_offset − margin) = 0`). Removing it gives **+3.71** reward on Anymal-D Newton (+16.38 vs A0 baseline +12.47) | | 5 | `terminations.py` — `body_lin_vel_out_of_limit` / `body_ang_vel_out_of_limit` + `__init__.pyi` exports | **DROPPED** | Were a NaN guard for the Newton `body_lin_vel` runaway when `base_com` was unguarded. With the `preset(newton=None)` gate (item 3), the runaway no longer occurs and the guards are unused | | 6 | `terrain_generator.py` subdivided flat-grid border | **DROPPED** | Was a workaround for Newton triangle-collision failures on the box-primitive border. Newton has since improved triangle handling, so the workaround is no longer needed | ## 2. New work — `2a532d1f745` ### 2.1 `NewtonShapeCfg` + `checked_apply` wiring The single most important Newton setting for rough terrain is **shape margin**. Without a nonzero margin, non-Anymal-D robots fail to learn stable contact on triangle-mesh terrain. The previous `NewtonManager.create_builder` only set `gap = 0.01` and left `margin` at Newton's upstream default of `0.0`. This PR adds `NewtonShapeCfg` (the Isaac Lab wrapper) exposing `margin` and `gap`, and forwards it onto Newton's upstream `ShapeConfig` via `checked_apply` from PR #5365: ```python @configclass class NewtonShapeCfg: margin: float = 0.0 gap: float = 0.01 # in NewtonCfg default_shape_cfg: NewtonShapeCfg = NewtonShapeCfg() # in NewtonManager.create_builder shape_cfg = cfg.default_shape_cfg if isinstance(cfg, NewtonCfg) else NewtonShapeCfg() checked_apply(shape_cfg, builder.default_shape_cfg) ``` `RoughPhysicsCfg` opts in to `default_shape_cfg=NewtonShapeCfg(margin=0.01)`. ### 2.2 Hoist `RoughPhysicsCfg` into shared base Octi's per-env Anymal-D `RoughPhysicsCfg` (MJWarp solver + collision pipeline) is hoisted into `LocomotionVelocityRoughEnvCfg.sim` so every rough-terrain env inherits identical Newton physics. Per-env files become minimal robot-only deltas. ### 2.3 Per-env Newton-only tweak - **Go1**: leg armature preset for joint stability on lightweight quadruped. ### 2.4 Mass randomization rewrite Replace `EventsCfg.add_base_mass`'s additive `(-5, 5)` kg default with multiplicative `(1/1.25, 1.25)` log-uniform scale (`operation="scale"`, `distribution="log_uniform"`). Scale-invariant across robot sizes, geometric mean 1.0, no per-robot kg overrides needed. Per-robot ablation, Newton, 1500-iter (small quadrupeds early-aborted at iter 300): | # | Robot | new log-uniform | old additive baseline | ratio | verdict | |---:|---|---:|---:|---|---| | 1 | A1 (iter 300) | 10.00 | 3.25 | **3.08×** | pass — driven by bias removal (old `(-1, 3)` had +10% mean bias on A1's 10 kg base) | | 2 | Go1 (iter 300) | 22.29 | 16.30 | 1.37× | pass | | 3 | Anymal-B (iter 300) | 12.47 | 10.92 | 1.14× | pass | | 4 | Anymal-C (iter 300) | 14.64 | 12.31 | 1.19× | pass | | 5 | Go2 @ 1499 | 24.71 | 18.58 | 1.33× | pass | | 6 | Anymal-D @ 1499 | 16.09 | 15.62 | 1.03× | pass | | 7 | H1 @ 1499 (biped) | 24.02 | 23.58 | 1.02× | pass | | 8 | Cassie @ 1499 sym25 | 14.15 | 23.93 (mass rand off) | 0.59× | **regression — fixed in dependent PR #5298 with `(1.0, 1.25)` heavier-only override** | Cassie sensitivity is closed-loop Achilles + hip PD response: lighter-than-nominal pelvis destabilizes; heavier-only `(1.0, 1.25)` recovers 90% of the reward and pushes ep_len higher (932 vs 910 baseline). Sub-ablation table is in #5298. Raw logs, checkpoints, and config snapshots preserved at `~/workspaces/data/2026-04-21_mass-rand-scale/` (per-robot `<robot>_newton_1500.log`, `key.md`, `status.md`, `run.sh` reproducer). ## Versions - `isaaclab_newton` 0.5.21 → 0.5.22 - `isaaclab_tasks` 1.5.24 → 1.5.25 ## Type of change - New feature (non-breaking). ## Checklist - [x] Pre-commit checks pass - [x] CHANGELOG + extension.toml bumped on both `isaaclab_newton` and `isaaclab_tasks` - [x] Co-author credit for [@ooctipus](https://github.com/ooctipus) on the cherry-pick commit - [x] Ablation evidence cited in commit messages --------- Signed-off-by: Kelly Guo <kellyg@nvidia.com> Co-authored-by: Octi Zhang <zhengyuz@nvidia.com> Co-authored-by: Kelly Guo <kellyg@nvidia.com>
Description
This PR depends on
#5219
#5179
#5098
Note:
Type of change
Screenshots
Please attach before and after screenshots of the change if applicable.
Checklist
pre-commitchecks with./isaaclab.sh --formatconfig/extension.tomlfileCONTRIBUTORS.mdor my name already exists there